#ifndef _MAP_HH_
#define _MAP_HH_
#include "Torus.hh"
#include "Square.h"
#include "Bug.h"
#include <vector>
#include <set>

/// Torus specialization for Ant maps
struct Map : public Torus
{
    double attackRadius, viewRadius, spawnRadius;
    unsigned myteam;

    // precomputed list of reach positions for attack
    std::vector<std::vector<uLocation> > attack_reach_map;
	
	// precomputed list of reach positions for food gathering
	std::vector<std::vector<uLocation> > food_reach_map;

    // precomputed list of reach positions for view
    std::vector<std::vector<uLocation> > view_reach_map;

    /// Class builder
    Map(unsigned _rows,unsigned _cols,double _attackRadius,double _viewRadius, double _spawnRadius, unsigned _myteam = 0) :
        Torus(_rows,_cols),
        attackRadius(_attackRadius), viewRadius(_viewRadius), spawnRadius(_spawnRadius),
        myteam(_myteam)
    {
        TRACK();

        bug << "new map, " << _rows << " * " << _cols << std::endl;
        bug << "init visibility reach map" << std::endl;
        istrue_t all;
        init_reach_map(view_reach_map,viewRadius,all);
        refresh_attack_and_spawn_reach_map();
    }

    /// used at begining and sometimes to remove water squares from reach_maps
    void refresh_attack_and_spawn_reach_map(void)
    {
        TRACK();
        isNotWater_t is_ground(*this);
        init_reach_map(attack_reach_map,attackRadius,is_ground);
		init_reach_map(food_reach_map,spawnRadius,is_ground);
    }

    // dummy always valid functor for flying
    struct istrue_t
    {
        inline bool operator()(uLocation loc) { return true; }
    };

    // is not water functor for ground positions
    struct isNotWater_t
    {
        Torus & grid;

        isNotWater_t(Torus & _grid) : grid(_grid) {}

        inline bool operator()(uLocation loc) { return !grid[loc].isWater; }
    };


    /// init reach map
    /// 'is_valid' functor may be used to avoid some positions
    template<typename _Valid>
    void init_reach_map(std::vector<std::vector<uLocation> > & reach_map,
                        double radius, _Valid is_valid)
    {
        TRACK();
        reach_map.assign(rows*cols, std::vector<uLocation>());

        for(uLocation sLoc = 0; sLoc!=rows*cols; ++sLoc)
            if(is_valid(sLoc))
            {
                std::set<uLocation> visited;
                std::queue<uLocation> locQueue;
                locQueue.push(sLoc);
                visited.insert(sLoc);

                while(!locQueue.empty())
                {
                    uLocation cLoc = locQueue.front();
                    locQueue.pop();
                    reach_map[sLoc].push_back(cLoc);

                    for(int d=0; d<Torus::TDIRECTIONS; d++)
                    {
                        uLocation nLoc = getLocation(cLoc, d);
                        if(!visited.count(nLoc) && is_valid(nLoc) && distance(sLoc,nLoc) <= radius)
                            locQueue.push(nLoc);
                        visited.insert(nLoc);
                    }
                }
            }
    }

    static Map * GlobalMap;
};


#endif /* _MAP_HH_ */
